import re
import os
import time
import shutil
import subprocess
from typing import List, Dict, Tuple, Optional
from pathlib import Path
from datetime import datetime
import requests
from tempfile import TemporaryDirectory
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

class DiffAnalyzer:
    """Diff分析工具类"""
    
    # 类变量，所有实例共享
    _last_request_time = 0
    _min_request_interval = 2.0  # 最小请求间隔2秒
    _lock = None  # 用于线程安全
    
    def __init__(self, debug_mode: bool = False):
        self.debug_mode = debug_mode
        self.session = self._create_limited_session()
        
        # 初始化线程锁（如果需要多线程）
        if DiffAnalyzer._lock is None:
            import threading
            DiffAnalyzer._lock = threading.Lock()
    
    def _create_limited_session(self) -> requests.Session:
        """创建带有限流和重试机制的session"""
        session = requests.Session()
        
        # 兼容不同版本的 urllib3
        retry_kwargs = {
            'total': 3,
            'backoff_factor': 1,
            'status_forcelist': [429, 500, 502, 503, 504],
        }
        
        # 检查 urllib3 版本并设置相应参数
        try:
            import urllib3
            urllib3_version = urllib3.__version__
            if urllib3_version >= '1.26.0':
                retry_kwargs['allowed_methods'] = ["GET"]
            else:
                retry_kwargs['method_whitelist'] = ["GET"]
        except (ImportError, AttributeError):
            # 如果无法获取版本信息，使用较新的参数名
            retry_kwargs['allowed_methods'] = ["GET"]
        
        retry_strategy = Retry(**retry_kwargs)
        
        adapter = HTTPAdapter(
            max_retries=retry_strategy,
            pool_connections=10,
            pool_maxsize=10
        )
        
        session.mount("http://", adapter)
        session.mount("https://", adapter)
        
        return session
    
    def _rate_limit(self):
        """实现全局请求速率限制"""
        with DiffAnalyzer._lock:  # 线程安全
            current_time = time.time()
            elapsed = current_time - DiffAnalyzer._last_request_time
            
            if elapsed < DiffAnalyzer._min_request_interval:
                sleep_time = DiffAnalyzer._min_request_interval - elapsed
                if self.debug_mode:
                    print(f"全局速率限制: 等待 {sleep_time:.2f} 秒")
                time.sleep(sleep_time)
            
            DiffAnalyzer._last_request_time = time.time()
    
    def download_pr_diff(self, repo_name: str, pr_number: int, access_token: str = None) -> str:
        """
        下载PR的diff内容
        
        Args:
            repo_name: 仓库名称（格式：owner/repo）
            pr_number: PR编号
            access_token: 访问令牌（可选）
            
        Returns:
            str: diff内容
        """
        self._rate_limit()  # 应用全局速率限制
        
        url = f"https://gitee.com/{repo_name}/pulls/{pr_number}.diff"
        
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        }
        if access_token:
            headers['Authorization'] = f'token {access_token}'
        
        try:
            response = self.session.get(url, headers=headers, timeout=30)
            response.raise_for_status()
            
            if self.debug_mode:
                print(f"下载diff成功，状态码: {response.status_code}")
                print(f"响应长度: {len(response.text)} 字符")
            
            return response.text
            
        except requests.exceptions.HTTPError as e:
            if e.response.status_code == 429:
                print("请求过于频繁，触发速率限制")
                retry_after = e.response.headers.get('Retry-After')
                if retry_after:
                    wait_time = int(retry_after)
                    print(f"需要等待 {wait_time} 秒后重试")
                    time.sleep(wait_time)
                    return self.download_pr_diff(repo_name, pr_number, access_token)
            print(f"HTTP错误: {e}")
            return ""
        except requests.exceptions.ConnectionError as e:
            print(f"连接错误: {e}")
            return ""
        except requests.exceptions.Timeout as e:
            print(f"请求超时: {e}")
            return ""
        except requests.exceptions.RequestException as e:
            print(f"请求异常: {e}")
            return ""
    
    def analyze_diff(self, diff_content: str) -> bool:
        """
        分析diff内容
        
        Args:
            diff_content: diff文本内容
            
        Returns:
            bool: 是否有新增文件或者有#include新增
        """
        if not diff_content:
            print("diff is null")
            return False
        
        if self.debug_mode:
            print(f"开始分析diff内容，长度: {len(diff_content)} 字符")
        
        # 检查是否有新增文件（包含 new file mode 的行）
        has_new_files = "new file mode" in diff_content
        if self.debug_mode:
            print(f"是否有新增文件: {has_new_files}")
        
        # 检查是否有+#include
        include_pattern = r'\+#include'
        matches = re.findall(include_pattern, diff_content)
        has_include_additions = len(matches) > 0
        
        if self.debug_mode:
            print(f"是否有+#include新增: {has_include_additions}")
            if has_include_additions:
                print(f"找到的+#include匹配数量: {len(matches)}")
                # 只显示前5个匹配以避免输出过多
                for i, match in enumerate(matches[:5]):
                    print(f"  匹配 {i+1}: {match}")
                if len(matches) > 5:
                    print(f"  还有 {len(matches) - 5} 个匹配...")
        
        # 满足其中一个条件就返回true
        return has_new_files or has_include_additions

    def save_diff(self, diff: str, pr_number: int, diff_dir: Path) -> str:
        """
        保存diff内容，以PR号命名
        如果文件已存在，则先删除再重新创建
        """
        print(f"start : save_diff")

        pr_diff_dir = diff_dir / f"pr_{pr_number}"
        if pr_diff_dir.is_dir():
            shutil.rmtree(pr_diff_dir)

        pr_diff_dir.mkdir(parents=True, exist_ok=True)

        try:
            filename = f"{pr_diff_dir}/pr_{pr_number}.diff"
            file_path = Path(filename)
            
            # 检查文件是否存在，如果存在则删除
            if file_path.exists():
                print(f"⚠️ 文件已存在，正在删除: {filename}")
                file_path.unlink()  # 删除文件
                print(f"✅ 已删除旧文件: {filename}")
            
            # 创建并写入新文件
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(diff)
            
            absolute_path = file_path.absolute()
            return str(absolute_path)
            
        except IOError as e:
            print(f"❌ 文件写入错误: {e}")
            raise
        except Exception as e:
            print(f"❌ 保存失败: {e}")
            raise

    @staticmethod
    def apply_diff_check(diff_file: str, target_dir: str) -> bool:
        """
        将 diff 文件应用到目标目录

        Args:
            diff_file: diff文件路径

        Returns:
            True 不冲突 , False 冲突
        """
        print(f"\n=== 检查diff: {Path(diff_file).name} 是否冲突  ===")

        # 检查diff文件是否存在
        if not Path(diff_file).exists():
            print(f"错误：diff文件不存在 {diff_file}")
            return False

        try:
            # 执行 patch 命令
            cmd = ['git', 'apply', diff_file, f'--directory={target_dir}',
                   '--check']
            print(f"执行命令: {' '.join(cmd)}")

            result = subprocess.run(
                cmd, capture_output=True, text=True, timeout=300)

            if result.returncode == 0:
                print(f"pr{diff_file} 无冲突")
                return True
            else:
                print(f"pr{diff_file} 冲突")
                return False

        except subprocess.TimeoutExpired:
            print("应用 diff 超时")
            return False
        except Exception as e:
            print(f"\n发生错误: {str(e)}")
            return False
        
    @staticmethod
    def apply_diff(diff_file: str, target_dir: str) -> bool:
        """
        将 diff 文件应用到目标目录

        Args:
            diff_file: diff文件路径

        Returns:
            True 不冲突 , False 冲突
        """

        # 检查diff文件是否存在
        if not Path(diff_file).exists():
            print(f"错误：diff文件不存在 {diff_file}")
            return False

        try:
            # 执行 patch 命令
            cmd = [
                'patch',
                '-p1',              # 跳过一级目录前缀
                '--directory', target_dir,  # 指定目标目录
                '-i', diff_file,     # 指定输入文件
                '--no-backup-if-mismatch',  # 不备份不匹配的文件
                '--forward'         # 即使已经应用过也继续
            ]
            print(f"执行命令: {' '.join(cmd)}")

            result = subprocess.run(
                cmd, capture_output=True, text=True, timeout=300)

            if result.returncode == 0:
                print(f"pr{diff_file} apply 成功")
                return True
            else:
                print(f"pr{diff_file}  {result.returncode} apply 失败")
                return False

        except subprocess.TimeoutExpired:
            print("应用 diff 超时")
            return False
        except Exception as e:
            print(f"\n发生错误: {str(e)}")
            return False

    def create_virtual_workspace(self, diff_dir:Path, work_dir:Path, base_dir:Path) -> bool:
        """创建虚拟工作空间并执行构建流程（使用后自动删除）"""
        print("\n=== 创建虚拟工作空间 ===")

        # 检查diff文件是否存在
        
        if not diff_dir.exists():
            print(f"错误：diff文件不存在 {str(diff_dir)}")
            return False
        
        try:
            # 使用 TemporaryDirectory，会在退出时自动删除
            with TemporaryDirectory(prefix="ace_workspace_", dir=work_dir) as temp_dir:
                temp_dir = Path(temp_dir)
                print(f"临时工作空间路径: {temp_dir}")
                print("⚠️ 注意：此工作空间将在流程完成后自动删除")

                # 1. 拷贝源代码
                workspace_ace = temp_dir / "ace_engine"
                print(f"\n拷贝源代码到: {workspace_ace}")
                
                if not base_dir.exists():
                    print(f"错误：基础代码目录不存在 {base_dir}")
                    return False
                
                # 确保目标目录不存在
                if workspace_ace.exists():
                    print(f"⚠️ 目标目录已存在，删除: {workspace_ace}")
                    shutil.rmtree(workspace_ace)
                
                shutil.copytree(base_dir, workspace_ace)
                print("✅ 源代码拷贝完成")

                # 2. 应用diff文件
                print("\n=== 应用diff文件 ===")
                diff_work_dir = workspace_ace / "foundation/arkui/ace_engine"
                
                # 确保目标目录存在
                if not diff_work_dir.exists():
                    print(f"创建目标目录: {diff_work_dir}")
                    return False
                
                if not self.apply_diff(str(diff_dir), str(diff_work_dir)):
                    print("应用diff失败，终止流程")
                    return False

                # 3. 执行构建/分析
                print("\n=== 执行头文件引用分析 ===")
                os.chdir(workspace_ace)
                
                # 检查脚本文件是否存在
                script_path = Path("get_hifle_counts.py")
                if not script_path.exists():
                    print(f"❌ 错误：分析脚本不存在 {script_path}")
                    return False

                # 执行头文件引用分析脚本
                build_cmd = [
                    "python", str(script_path),
                    "foundation/arkui/ace_engine",
                    "--exclude_dirs", "foundation/arkui/ace_engine/test",
                    "foundation/arkui/ace_engine/adapter/preview/",
                    "--include_dirs", "./",
                    "foundation/arkui/ace_engine",
                    "foundation/arkui/ace_engine/frameworks/",
                    "foundation/arkui/ace_engine/interfaces/inner_api/ace_kit/include/",
                    "--output",
                    "diff_hfile_counts.txt"
                ]

                print(f"执行命令: {' '.join(build_cmd)}")
                result = subprocess.run(
                    build_cmd, capture_output=True, text=True, timeout=600)

                if result.returncode != 0:
                    print(f"分析执行失败 (返回码 {result.returncode}):")
                    print("标准输出:", result.stdout)
                    print("错误输出:", result.stderr)
                    return False

                print("✅ 分析执行成功")
                if result.stdout.strip():
                    print("输出:")
                    print(result.stdout)

                # 4. 收集输出文件
                print("\n=== 收集分析结果 ===")
                

                file = Path("diff_hfile_counts.txt")
                if file.exists():
                    os.system(f"cp {file.name} {diff_dir.parent / file.name}")
                else:
                    return False
                

            # TemporaryDirectory 会在退出 with 块时自动删除
            print("\n✅ 虚拟工作空间流程完成")
            print("⚠️ 临时工作空间已自动删除")
            return True
        except Exception as e:
            print(f"\n❌ 创建虚拟工作空间时发生错误: {str(e)}")
            import traceback
            traceback.print_exc()
            return False
        
    @staticmethod
    def parse_file(filename: Path) -> Dict[str, Tuple[str, Optional[str]]]:
        """解析文件，返回一个字典，key是:前的部分，value是整行内容和:后的数字"""
        result = {}
        try:
            with open(filename, 'r', encoding='utf-8') as f:
                for line in f:
                    line = line.strip()
                    if ':' in line:
                        key, value = line.split(':', 1)
                        result[key] = (line, value.strip())
                    else:
                        result[line] = (line, None)
        except FileNotFoundError:
            print(f"警告：文件不存在 {filename}")
        except Exception as e:
            print(f"解析文件 {filename} 时出错: {e}")
        return result

    def compare_files(self, basePath: Path, diffPath: Path, pr_number: int) -> Path:
        """
        比较两个文件并生成结果文件

        Args:
            file1: 第一个文件路径
            file2: 第二个文件路径
            output_name: 输出文件名

        Returns:
            生成的比较结果文件路径
        """
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        output_name = f"{diffPath.parent}{pr_number}_result_{timestamp}.txt"

        output_file = Path(output_name)

        dict1 = self.parse_file(basePath)
        dict2 = self.parse_file(diffPath)

        with open(output_file, 'w', encoding='utf-8') as out:
            # 写入文件信息
            out.write(f"比较文件: {basePath.name} 和 {diffPath.name}\n")
            out.write(
                f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            out.write("=" * 50 + "\n\n")

            # 检查新增或修改的行
            out.write("=== 新增或修改的行 ===\n")
            changes_found = False
            for key in dict1:
                line1, value1 = dict1[key]
                if key in dict2:
                    line2, value2 = dict2[key]
                    if value1 != value2:
                        out.write(
                            f"修改: {line1} (原值: {value2 if value2 is not None else '无'})\n")
                        changes_found = True
                else:
                    out.write(f"新增: {line1}\n")
                    changes_found = True

            if not changes_found:
                out.write("无新增或修改的行\n")

            # 检查移除的行
            out.write("\n=== 移除的行 ===\n")
            removals_found = False
            for key in dict2:
                if key not in dict1:
                    line2, value2 = dict2[key]
                    out.write(f"移除: {line2}\n")
                    removals_found = True

            if not removals_found:
                out.write("无移除的行\n")

        print(f"比较结果已保存到: {output_file}")
        return output_file